/*
        Author: Nathan Newman
        File Name: pcbCommands.c
        Description: Accompanying commands for pcbs
        Last updated: 9/21/11

*/

#include <string.h>
#include "mpx_supt.h"
#include "commhand.h"
#include "pcbCom.h"
#include "r3code.h"

/*interface with command handler, parse command line arguments, etc. */
int getPcbCommand(char args[MAX_NARGS][MAX_ARGLEN], int nArgs){
	//is this a recognized command?
	if(nArgs<1) return 0;
	if(strcmp(args[0],"PS")!=0 && strcmp(args[0],"PROCESSES")!=0) return 0;
	
	//it's recognized as a ps command, so check additional syntax
	if(nArgs<2||nArgs>5) {printSyntaxError(); return 1;}
	if(nArgs==2) {
		if(strcmp(args[1],"SHOWALL")==0) show_all_pcbs();
		else printSyntaxError();
	}
	if(nArgs == 3){
		if(strcmp(args[1],"SHOWALL")==0 && strcmp(args[2],"READY")==0) show_ready_pcbs();	
		else if(strcmp(args[1],"SHOWALL")==0 && strcmp(args[2],"BLOCKED")==0) show_blocked_pcbs();
		else if(strcmp(args[1],"SHOW")==0) show_pcb(args[2],0);
		else if(strcmp(args[1],"SHOWX")==0) show_pcb(args[2],1);
		else if(strcmp(args[1],"BLOCK")==0) block_pcb(args[2]);
		else if(strcmp(args[1],"UNBLOCK")==0) unblock_pcb(args[2]);
		else if(strcmp(args[1],"DELETE")==0) delete_pcb(args[2]);		
		else if(strcmp(args[1],"RESUME")==0) {
			if(resume_pcb(args[2])==ERR_PCB_NOTFOUND) printf("Error: could not find process %s\n",args[2]); 
			else printf("Process %s has resumed.\n",args[2]);
		}
		else if(strcmp(args[1],"SUSPEND")==0) {
			if(suspend_pcb(args[2])==ERR_PCB_NOTFOUND) printf("Error: unable to find process %s\n",args[2]);
			else printf("Process %s has been suspended.\n",args[2]);
		}
		else printSyntaxError();
	}
	if(nArgs == 4) {
		if(strcmp(args[1],"CHPRIORITY")!=0) printSyntaxError(); 
		else {
			int i;
			if(getPriority(args[3],&i) == 0) return 1;
			if(find_pcb(args[2])){
				set_pcb_priority(args[2],i);
				printf("%s's priority successfully updated to %d.\n",args[2],i); 	
			}
			else printf("Error: unable to find process %s.\n",args[2]);
		}
	}
	if(nArgs == 5) {
		int i;
		if(strcmp(args[1],"CREATE") != 0) {printSyntaxError(); return 1;}
		
		if(getPriority(args[3],&i) == 0) return 1;
		
		if(strcmp(args[4],"SYSTEM")==0) create_pcb(args[2],i,SYSTEM);
		else if(strcmp(args[4],"APPLICATION")==0) create_pcb(args[2],i,APPLICATION);
		else printf("Error: unrecognized class.\nAcceptable pcb classes include SYSTEM and APPLICATION.\n");
	}
	return 1;
}

void printSyntaxError(){
	printf("Invalid argument syntax for process-displaying commands.\n"
				"Acceptable commands include\n"
				"PS SHOWALL [(READY|BLOCKED)]\n"
				"PS (SHOW|SHOWX|BLOCK|UNBLOCK|RESUME|SUSPEND|DELETE) <process name>\n"
				"PS CHPRIORITY <process name> <priority value>\n"
				"PS CREATE <process name> <priority value> <class>\n");
}

/* return 1 if priority is valid; 0 if not */
int getPriority(char* strPriority, int* priority){
	int i, priStrLen = strlen(strPriority);
	if(priStrLen > 4) printf("Error: invalid priority\nValues should be integers between -128 and 127\n");
	else {
		for(i = 0; i<priStrLen; i++) {
			//check that the digits are all numbers, except possibly - sign at front
			switch (strPriority[i]){
				case '9': break;
				case '8': break;
				case '7': break;
				case '6': break;
				case '5': break;
				case '4': break;
				case '3': break;
				case '2': break;
				case '1': break;
				case '0': break;
				case '-': if(i==0) break;
				default:
					printf("Error: invalid priority\nValues should be integers between -128 and 127\n");
					return 0;
			}
		}
		i = atoi(strPriority);
		if(i < MIN_PRIORITY || i > MAX_PRIORITY) printf("Error: invalid priority\nValues should be integers between -128 and 127\n");
		else {*priority = i; return 1;}
	}
	return 0;
}

int print_pcb(pcb* pcbPtr, int isExtended){
	char tempClass[15];
	char tempIsSuspended[15];
	char tempState[15];
	
	switch (pcbPtr->class) {
		case APPLICATION: strcpy(tempClass,"APPLICATION"); break;
		case SYSTEM: strcpy(tempClass,"SYSTEM"); break;
		default: strcpy(tempClass,"UNKNOWN"); break;
	}
	switch (pcbPtr->state){
		case RUNNING: strcpy(tempState,"RUNNING"); break;
		case READY: strcpy(tempState,"READY"); break;
		case BLOCKED: strcpy(tempState,"BLOCKED"); break;
		default: strcpy(tempState,"UNKNOWN"); break;
	}
	if(pcbPtr->isSuspended) strcpy(tempIsSuspended,"TRUE"); else strcpy(tempIsSuspended,"FALSE");
	
	printf("Name: %s\nClass: %s\nPriority: %d\nSuspended: %s\nState: %s\n",
			pcbPtr->name,tempClass,pcbPtr->priority,tempIsSuspended,tempState);
	
	if(isExtended){
		context* c = (context*) pcbPtr->stackTop;
		
		if(pcbPtr->loadAddr) printf("loadAddr = %p, ",pcbPtr->loadAddr);
		else printf("loadAddr = NULL, ");
		if(pcbPtr->execAddr) printf("execAddr = %p\n",pcbPtr->execAddr);
		else printf("execAddr = NULL\n");
		
		printf( 
			"BP = %u, DI = %u, SI = %u, DS = %u, ES = %u\n"
			"DX = %u, CX = %u, BX = %u, AX = %u, FLAGS = %u\n"
			"IP = %u,CS = %u\n",
			c->BP,c->DI,c->SI,c->DS,c->ES,
			c->DX,c->CX,c->BX,c->AX,c->FLAGS,
			c->IP,c->CS
		);
	}
	return 0;
}


int showQ(enum pcbState s){
	int count, numLines = 3, linesPerProcess = 6;
	pcb* toPrintPtr;
	q* qPtr = getQ(s);
	if(!qPtr) return -1;
	if((count=qPtr->size) < 1) return 0;
	toPrintPtr = qPtr->qFirstPtr;
	while(toPrintPtr && !pager(&numLines,linesPerProcess)){
		print_pcb(toPrintPtr,0);
		printf("\n");
		toPrintPtr = toPrintPtr->backPtr;
	}
	return count;
}

//|---------------------PROBABLY PERMANENT commands------------------|\\
//Note: these error-returning commands return 0 upon success and a negative error value otherwise.


int suspend_pcb(char* pname){
	pcb *pcbPtr = find_pcb(pname);
	if(pcbPtr == 0) return ERR_PCB_NOTFOUND;
	pcbPtr->isSuspended = TRUE;
	return 0;
}
int resume_pcb(char* pname){
	pcb *pcbPtr = find_pcb(pname);
	if(pcbPtr == 0) return ERR_PCB_NOTFOUND;
	pcbPtr->isSuspended = FALSE;
	return 0;
}
int set_pcb_priority(char* pname, int priority){
	pcb* pcbPtr;
	if(priority < -128 || priority > 127) return ERR_BAD_PRIORITY;
	pcbPtr = find_pcb(pname);
	if(pcbPtr == 0) return ERR_PCB_NOTFOUND;
	pcbPtr->priority=priority;
	if(pcbPtr->state == READY){
		//place it in priority order
		remove_pcb(pcbPtr);
		insert_pcb(pcbPtr,getQ(READY));
	}
	return 0;
}
int show_pcb(char* pname,int isExtended){
	pcb *pcbPtr = find_pcb(pname);
	if(!pcbPtr) {
		printf("Error: could not find process %s.\n",pname);
		return ERR_PCB_NOTFOUND;
	}
	print_pcb(pcbPtr,isExtended);
	return 0;
}
int show_all_pcbs(){
	int numLinesPerPage = MAX_LINES_PER_PAGE;
	int count1 = getQ(READY)->size, count2 = getQ(BLOCKED)->size; 
	int pageBetweenQs = (count1*count2 && (count1+count2)>4); //page break between queues if both are nonempty and more than 4 total processes
	printf("ALL PROCESSES\n"
		   "-------------\n\n");
	showQ(READY);
	if(pageBetweenQs) pager(&numLinesPerPage,0);
	showQ(BLOCKED);
	count1 += count2;
	printf("Total: %d processes\n",count1);
	return count1;
}
int show_ready_pcbs(){
	int count;
	printf("READY PROCESSES\n"
		   "---------------\n\n");
	count = showQ(READY);
	printf("Total: %d READY processes\n",count);
	return 0;
}
int show_blocked_pcbs(){
	int count; 
	printf("BLOCKED PROCESSES\n"
		   "-----------------\n\n");
	count = showQ(BLOCKED);
	printf("Total: %d BLOCKED processes\n",count);
	return count;
}



//|---------------------PROBABLY TEMPORARY commands------------------|\\ 

/* allocate and setup, or display error */
int create_pcb(char* pname,int p,enum pcbClass c){
	pcb *newPcbPtr = setup_pcb(pname,p,c);
	if(!newPcbPtr) {printf("Error: process %s already exists.\n",pname); return -1;}
	if(insert_pcb(newPcbPtr,getQ(READY))<0) {printf("Error: unable to successfully insert pcb into ready queue.");return -1;}
	printf("Process %s has been successfully created and inserted into READY queue.\n",pname);
	return 0;
}

/* delete and free pcb, or display error */
int delete_pcb(char* pname){
	int remerr = 0, delerr = 0, err = 0; 
	pcb *toDeletePtr = find_pcb(pname);
	if(!toDeletePtr) {
		printf("Error: unable to find process %s.\n",pname);
		return -1;
	}
	remerr = remove_pcb(toDeletePtr);
	delerr = free_pcb(toDeletePtr);
	if(remerr<0) printf("Error: unable to successfully remove pcb\n");
	if(delerr<0) printf("Error: unable to successfully delete pcb\n");
	if(!(err = remerr+delerr)) printf("Process %s has been successfully deleted.\n",pname);
	return err;
}

/* update the pcb's state to BLOCKED */
int block_pcb(char* pname){
	int err = 0;
	pcb *toBlockPtr = find_pcb(pname);
	if(!toBlockPtr) {
		printf("Error: unable to find process %s.\n",pname);
		return -1;
	}
	if(toBlockPtr->state == BLOCKED) {printf("Error: already blocked\n"); return ERR_ALREADY_BLOCKED;}
	err = remove_pcb(toBlockPtr);
	if(err<0) {printf("Error: unable to successfully remove process from  READY queue\n");return err;}
	toBlockPtr->state = BLOCKED;
	err = insert_pcb(toBlockPtr,getQ(BLOCKED));
	if(err<0) {printf("Error: unable to successfully insert process into BLOCKED queue\n");return err;}
	printf("Process %s has been blocked.\n",pname);
	return 0;
}	

/* update the pcb's state to READY */
int unblock_pcb(char* pname) {
	int err = 0; 
	pcb *toBlockPtr = find_pcb(pname);
	if(!toBlockPtr) {
		printf("Error: unable to find process %s.\n",pname);
		return -1;
	}
	if(toBlockPtr->state != BLOCKED) {printf("Error: already unblocked\n");return ERR_ALREADY_UNBLOCKED;}
	err = remove_pcb(toBlockPtr);
	if(err<0) {printf("Error: unable to successfully remove process from  BLOCKED queue\n");return err;}
	toBlockPtr->state = READY;
	err = insert_pcb(toBlockPtr,getQ(READY));
	if(err<0) {printf("Error: unable to successfully insert process into READY queue\n");return err;}
	printf("Process %s has been unblocked.\n",pname);
	return 0;
}



